// 13.3 交换操作
/**
 * 除了定义拷贝控制成员，管理资源的类通常还定义一个名为swap的函数（参见9.2.5节，第303页）。对于那些与重排元素顺序的算法（参见10.2.3节，第342页）一起使用的类，定义swap是非常重要的。
 * 如果一个类定义了自己的swap，那么算法将使用类自定义版本。否则，算法将使用标准库定义的swap。
 */

#include <iterator>
#include <vector>
#include <list>
#include <deque>
#include <forward_list>
#include <string>
#include <array>
#include <stack>
#include <queue>
#include <algorithm>
#include <numeric>
using std::swap;
using std::vector, std::list, std::deque, std::forward_list, std::string, std::array, std::stack, std::queue;
#include "../Chapter07/Sales_data.h"
#include <iostream>
using std::begin, std::cbegin, std::end, std::cend, std::find, std::accumulate, std::equal, std::fill, std::fill_n, std::back_inserter;
using std::cin, std::cout, std::endl;
using std::copy, std::replace, std::replace_copy;
#include <map>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <memory>
#include <new>
using namespace std;

// 类值版本的HasPtr
class HasPtr
{
    friend void swap(HasPtr&, HasPtr&); // 将swap定义为friend，以便能访问HasPtr的（private的）数据成员
public:
    HasPtr(const string &s = string()) : ps(new string(s)), i(0) {}
    HasPtr(const HasPtr &p) : ps(new string(*p.ps)), i(p.i) {} // 对ps指向的string，每个HasPtr对象都有自己的拷贝
    // HasPtr &operator=(const HasPtr &);                         // 声明赋值运算符，但还未定义
    ~HasPtr() { delete ps; }
    HasPtr &operator=(HasPtr);

private:
    string *ps;
    int i;
};
// // 定义赋值运算符
// HasPtr &HasPtr::operator=(const HasPtr &rhs)
// {
//     auto newp = new string(*rhs.ps); // 拷贝底层string
//     delete ps;                       // 释放旧内存
//     ps = newp;                       // 从右侧运算对象拷贝数据到本对象
//     i = rhs.i;
//     return *this;
// }
inline // 由于swap的存在就是为了优化代码，我们将其声明为inline函数（参见6.5.2节，第213页）。
void swap(HasPtr &lhs, HasPtr &rhs)
{
    using std::swap;
    swap(lhs.ps, rhs.ps); // 交换指针，而不是交换string数据
    swap(lhs.i, rhs.i);   // 交换int成员
}
// 在这个版本的赋值运算符中，参数并不是一个引用，我们将右侧运算对象以传值方式传递给了赋值运算符。因此，rhs是右侧运算对象的一个副本。
// 注意rhs是按值传递的，意味着HasPtr的拷贝构造函数将右侧运算对象中的string拷贝到rhs
HasPtr& HasPtr::operator=(HasPtr rhs)
{
    swap(*this,rhs); // rhs现在指向本对象曾经使用的内存
    return *this;    // rhs被销毁，从而delete了rhs中的指针
}

int main()
{
    // 13.3
    // 编写我们自己的swap函数
    // 如果一个类定义了自己的swap，那么算法将使用类自定义版本。否则，算法将使用标准库定义的swap。
    // 与拷贝控制成员不同，swap并不是必要的。但是，对于分配了资源的类，定义swap可能是一种很重要的优化手段。

    // swap函数应该调用swap，而不是std::swap
    // 在本例中，数据成员是内置类型的，而内置类型是没有特定版本的swap的，所以在本例中，对swap的调用会调用标准库std：：swap。

    // 在赋值运算符中使用swap
    // 定义swap的类通常用swap来定义它们的赋值运算符。这些运算符使用了一种名为拷贝并交换（copy and swap）的技术。这种技术将左侧运算对象与右侧运算对象的一个副本进行交换：
    // 这个技术的有趣之处是它自动处理了自赋值情况且天然就是异常安全的。
    // 它通过在改变左侧运算对象之前拷贝右侧运算对象保证了自赋值的正确，这与我们在原来的赋值运算符中使用的方法是一致的（参见13.2.1节，第453页）。
    // 它保证异常安全的方法也与原来的赋值运算符实现一样。

    // 使用拷贝和交换的赋值运算符自动就是异常安全的，且能正确处理自赋值。
    HasPtr hp1,hp2;
    swap(hp1,hp2);
    hp2 = hp2;

    
    return 0;
}